package com.qidian.buy.cms.service.impl;

import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
import com.baomidou.mybatisplus.core.metadata.IPage;
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
import com.github.pagehelper.PageHelper;
import com.github.pagehelper.PageInfo;
import com.qidian.buy.cms.entity.Content;
import com.qidian.buy.cms.entity.ContentCategory;
import com.qidian.buy.cms.mapper.ContentCategoryMapper;
import com.qidian.buy.cms.mapper.ContentMapper;
import com.qidian.buy.cms.service.IContentCategoryService;
import com.qidian.buy.cms.service.IContentService;
import com.qidian.buy.util.ThreadPoolTools;
import com.qidian.buy.util.Tools;
import com.qidian.buy.util.pojo.QdResult;
import org.apache.commons.lang3.StringUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.cloud.context.config.annotation.RefreshScope;
import org.springframework.data.redis.core.StringRedisTemplate;
import org.springframework.stereotype.Service;

import java.util.ArrayList;
import java.util.List;
import java.util.Random;
import java.util.concurrent.ThreadPoolExecutor;
import java.util.concurrent.TimeUnit;

/**
 * <p>
 *  服务实现类
 * </p>
 *
 * @author 戴着假发的程序员
 * @since 2024-05-06
 */
@Service
@RefreshScope// 让后端修改配置文件，自动更新
public class ContentServiceImpl  implements IContentService {
    @Autowired
    private ContentMapper contentMapper;
    @Autowired
    private IContentCategoryService contentCategoryService ;
    @Autowired
    private ContentCategoryMapper contentCategoryMapper;
    @Autowired
    private StringRedisTemplate stringRedisTemplate;

    @Value("${CONTENT_PRE}")
    private String contentPre; // 缓存前缀
    @Value("CACHE_DELAY_TIME")
    private String cacheDelayTime; // 缓存清理的延迟时间
    @Value("CACHE_CLEAR_THREADPOOLNAME")
    private String cacheClearThreadPoolName;// 缓存清理的线程池名称

    @Override
    public QdResult<PageInfo<Content>> listByPageAndContent(int page, int limit, Content content) {
        PageHelper.startPage(page,limit);
        PageInfo<Content> pageInfo = new PageInfo<>(contentMapper.list(content));
        return QdResult.success(pageInfo);
    }

    @Override
    public QdResult save(Content content) {
        // 处理缓存
        // 查询内容类型
        ContentCategory contentCategory = contentCategoryMapper.selectById(content.getCatId());
        String key = contentPre + contentCategory.getCatMark();
        if(stringRedisTemplate.hasKey(key)){
            // 删除数据
            stringRedisTemplate.delete(key);
        }
        // 新增操作
        int insert = contentMapper.insert(content);
        if(insert == 1){
            //  启动子线程再次删除缓存
            // 获得线程池
            // 实际的工作中，线程池的获取可能有多个重载的方法，通过重载的方法可以获取补充尺寸和类型的线程池
            ThreadPoolExecutor pool = ThreadPoolTools.getPool(cacheClearThreadPoolName);
            // 提交任务
            pool.execute(() -> {
                // 等一会再次删除缓存
                try {
                    TimeUnit.MILLISECONDS.sleep(new Long(cacheDelayTime));
                    stringRedisTemplate.delete(key);
                } catch (InterruptedException e) {
                    throw new RuntimeException(e);
                }
            });
        }
        return QdResult.isSuccess(insert);
    }

    @Override
    public QdResult<IPage<Content>> queryByPage(Content content, int pageNumber, int limit) {
        IPage<Content> page = new Page<>(pageNumber,limit);
        QueryWrapper<Content> wrapper = new QueryWrapper<>();
        if(content != null) {
            // 普通条件
            wrapper.like(!StringUtils.isBlank(content.getText1()),"text1",content.getText1());
            wrapper.like(!StringUtils.isBlank(content.getText2()),"text2",content.getText2());
            wrapper.eq(content.getStatus() != null && !content.getStatus().equals(new Integer(0)),"status",content.getStatus());
            // 类别条件
            Long catId = content.getCatId();
            if(catId != null) {
                // 判断当前的对象师傅类别还是子类别
                ContentCategory category = contentCategoryService.getById(catId);
                if (category.getParent().equals(new Integer(0))) {// 说明当前的类别编号就是一个子类别
                    wrapper.eq("cat_id", catId);
                } else {
                    // 查询所有的子类别
                    List<Long> allChildCatId = getAllChildCatId(contentCategoryService.queryByParentId(catId));
                    // 设置条件
                    wrapper.in("cat_id", allChildCatId);
                }
            }
        }
        return QdResult.success(contentMapper.selectPage(page, wrapper));
    }

    @Override
    public QdResult<List<Content>> listByCarMark(String catMark, int count) {
        // 先查询缓存 // 我们的缓存前缀contentPre是配置在配置文件中，通过@value获取的
        String key = contentPre + catMark;
        if(stringRedisTemplate.hasKey(key)){
            String json = stringRedisTemplate.opsForValue().get(key);
            if(!StringUtils.isBlank(json)){
                // JSON转换为集合对象
                try {
                    List<Content> contents = Tools.json2list(json, Content.class);
                    // 转换成功，就直接返回数据
                    return QdResult.success(contents);
                } catch (Exception e) {
                    // 将缓存清理掉
                    stringRedisTemplate.delete(key);
                }
            }
        }
        // 如果缓存没有就查询数据库
        List<Content> contents = contentMapper.listByCarMark(catMark, count);
        if(contents == null || contents.size() == 0){
            // 为了防止缓存击穿
            // 如果没有查询到数据，就缓存一个null
            stringRedisTemplate.opsForValue().set(key, null);
            // 设置存活时间，这里的时间要短点
            stringRedisTemplate.expire(key,20, TimeUnit.SECONDS);
        }else {
            // 将查询的数据缓存起来
            stringRedisTemplate.opsForValue().set(key, Tools.object2json(contents));
            // 为了防止缓存雪崩
            // 设置存活时间
            stringRedisTemplate.expire(key, 60 + new Random().nextInt(60), TimeUnit.MINUTES);
        }
        return QdResult.success(contents);
    }

    /**
     * 递归的从一个类别集合中查找所有的子类别集合
     * @param cats
     * @return
     */
    private List<Long> getAllChildCatId(List<ContentCategory> cats){
        List<Long> catIds = new ArrayList<>();
        for (ContentCategory cat : cats) {
            // 判断cat是否是福类别
            if(cat.getParent().equals(new Integer(0))){
                catIds.add(cat.getCatId());// 当前的cat就是子类别
            }else{
                // 递归调用
                List<Long> allChildCatId = getAllChildCatId(cat.getChildren());
                catIds.addAll(allChildCatId);
            }
        }
        return catIds;
    }

}
